/*-------------------------------------------------------------------------
 *
 * superuser.c
 *	  superuser()函数。确定用户是否拥有超级用户特权。
 *
 * 所有代码都应使用这两个函数中的任意一个来查找
 * 给定用户是否是超级用户，而不是直接检查
 * pg_authid.rolsuper，以便为单用户情况内置的逃生口能正常工作。
 *
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, 加利福尼亚大学董事会
 *
 *
 * IDENTIFICATION
 *	  src/backend/utils/misc/superuser.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/htup_details.h"
#include "catalog/pg_authid.h"
#include "miscadmin.h"
#include "utils/inval.h"
#include "utils/syscache.h"

/*
 * 在一般情况下，相同的roleid（即会话或当前ID）将会被重复查询。所以我们维护一个简单的单项缓存，用于存储最后请求的roleid的状态。缓存可以通过监听pg_authid上的缓存更新事件在需要时被清除。
 */
static Oid	last_roleid = InvalidOid;	/* InvalidOid == 缓存无效 */
static bool last_roleid_is_super = false;
static bool roleid_callback_registered = false;

static void fc_RoleidCallback(Datum fc_arg, int fc_cacheid, uint32 fc_hashvalue);


/*
 * 运行此命令的Postgres用户具有Postgres超级用户权限
 */
bool superuser(void)
{
	return superuser_arg(GetUserId());
}


/*
 * 指定的角色具有Postgres超级用户权限
 */
bool superuser_arg(Oid fc_roleid)
{
	bool		fc_result;
	HeapTuple	fc_rtup;

	/* 对于缓存命中快速返回 */
	if (OidIsValid(last_roleid) && last_roleid == fc_roleid)
		return last_roleid_is_super;

	/* 如果您删除了所有用户，这是特别的逃生路径。 */
	if (!IsUnderPostmaster && fc_roleid == BOOTSTRAP_SUPERUSERID)
		return true;

	/* 好的，查找pg_authid中的信息 */
	fc_rtup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(fc_roleid));
	if (HeapTupleIsValid(fc_rtup))
	{
		fc_result = ((Form_pg_authid) GETSTRUCT(fc_rtup))->rolsuper;
		ReleaseSysCache(fc_rtup);
	}
	else
	{
		/* 对于无效的roleid报告“不是超级用户” */
		fc_result = false;
	}

	/* 如果是第一次，则设置缓存清除的回调 */
	if (!roleid_callback_registered)
	{
		CacheRegisterSyscacheCallback(AUTHOID,
									  fc_RoleidCallback,
									  (Datum) 0);
		roleid_callback_registered = true;
	}

	/* 为下次缓存结果 */
	last_roleid = fc_roleid;
	last_roleid_is_super = fc_result;

	return fc_result;
}

/*
 * RoleidCallback
 *		Syscache无效回调函数
 */
static void fc_RoleidCallback(Datum fc_arg, int fc_cacheid, uint32 fc_hashvalue)
{
	/* 如果角色的超级用户身份改变，失效我们的本地缓存 */
	last_roleid = InvalidOid;
}
